package cn.achao.csr.openapi.core.utils;


import cn.achao.csr.api.model.entity.RestBean;
import cn.achao.csr.api.model.entity.SysApi;
import cn.achao.csr.api.model.entity.SysApi;
import cn.achao.csr.api.model.enums.ErrorCode;
import cn.achao.csr.base.constant.enums.SysSourceEnum;
import cn.achao.csr.base.exception.BusinessException;
import cn.achao.csr.base.utils.ThrowUtils;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author HeD
 * @date 2022/9/7 17:17
 */
@Slf4j
@Component
public class RestUtils {

    /**
     * 配置工厂
     *
     * @param timeout 超时时间
     * @return
     */
    public static RestTemplate getRestTemplate(Integer timeout) {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(timeout);
        requestFactory.setReadTimeout(timeout);
        return new RestTemplate(requestFactory);
    }

    public static RestBean postJson(SysApi api, String json, HttpHeaders httpHeaders) {
        String address = api.getAddress();
        int timeout = Optional.ofNullable(api.getTimeout()).orElse(10000);

        //请求头
        RestTemplate restTemplate = getRestTemplate(timeout);
        ResponseEntity<String> exchange = exchange(restTemplate, httpHeaders, address, HttpMethod.POST, json, api);
        return getRestBean(exchange);
    }

    public static RestBean postForm(SysApi api, Map<String, Object> map, HttpHeaders httpHeaders) {
        httpHeaders.setContentType(MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE));
        AtomicReference<String> stringAtomicReference = new AtomicReference<>("");
        map.forEach((k, v) -> stringAtomicReference.set(stringAtomicReference + "&" + k + "=" + v));
        String uri = stringAtomicReference.get();
        if (uri.startsWith("&")) {
            uri = uri.substring(1);
            uri = "?" + uri;
        }
        return postForm(api, uri, httpHeaders);
    }

    public static RestBean postForm(SysApi api, String json, HttpHeaders httpHeaders) {
        String address = api.getAddress();
        int timeout = Optional.ofNullable(api.getTimeout()).orElse(10000);

        //请求头
        RestTemplate restTemplate = getRestTemplate(timeout);
        ResponseEntity<String> exchange = exchange(restTemplate, httpHeaders, address, HttpMethod.POST, json, api);
        return getRestBean(exchange);
    }

    public static RestBean getRestful(SysApi api, Map<String, Object> map, HttpHeaders headerInfo) {

        AtomicReference<String> stringAtomicReference = new AtomicReference<>("");
        map.forEach((s, o) -> {
            if (o != null) {
                stringAtomicReference.set("/" + map.get(s));
            }
        });
        String uri = stringAtomicReference.get();
        return get(api, uri, headerInfo);
    }

    public static RestBean get(SysApi api, String uri, HttpHeaders header) {
        String address = api.getAddress() + uri;
        int timeout = Optional.ofNullable(api.getTimeout()).orElse(5000);

        //请求头
        RestTemplate restTemplate = getRestTemplate(timeout);
        ResponseEntity<String> exchange = exchange(restTemplate, header, address, HttpMethod.GET, "", api);
        return getRestBean(exchange);
    }

    public static RestBean getRestBean(ResponseEntity<String> exchange) {
        RestBean restBean = new RestBean();
        if (exchange == null) {
            restBean.setCode(500);
            restBean.setResp("对外请求异常");
        } else {
            restBean.setCode(exchange.getStatusCodeValue());
            restBean.setResp(exchange.getBody());
        }
        return restBean;
    }

    public static String formData(SysApi api, HttpHeaders httpHeaders, MultiValueMap<String, Object> map) {
        String url = api.getAddress();
        int timeout = Optional.ofNullable(api.getTimeout()).orElse(10000);
        RestTemplate restTemplate = getRestTemplate(timeout);
        //头部类型
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        //构造实体对象
        HttpEntity<MultiValueMap<String, Object>> param = new HttpEntity<>(map, httpHeaders);
        //发起请求,服务地址，请求参数，返回消息体的数据类型
        ResponseEntity<String> response = restTemplate.postForEntity(url, param, String.class);
        //body
        return response.getBody();
    }


    private static ResponseEntity<String> exchange(RestTemplate restTemplate, HttpHeaders httpHeaders, String address,
                                                   HttpMethod method, String json, SysApi api) {
        ResponseEntity<String> exchange = null;
        String body;
        ThrowUtils.throwIf(StringUtils.isBlank(address),
                BusinessException.fail("address地址为空", SysSourceEnum.CSR_API.getSysName()));

        //数据库配置优先级最高 其次为配置文件
        String headerInfo = api.getHeaderInfo();
        if (StringUtils.isNotBlank(headerInfo)) {
            JSONObject jsonObject = JSONUtil.parseObj(headerInfo);
            if (jsonObject.size() > 0) {
                jsonObject.forEach((k, v) -> {
                    v = ObjectUtil.isEmpty(v) ? "" : v;
                    httpHeaders.set(k, (String) v);
                });
            }
        }
        if (ObjectUtil.isEmpty(httpHeaders.getContentType())) {
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        }
        HttpEntity<String> requestEntity = new HttpEntity<>(json, httpHeaders);

        long beginTime = System.currentTimeMillis();
        try {
            if (httpHeaders.containsKey("POST")) {
                method = HttpMethod.POST;
            }
            exchange = restTemplate.exchange(address, method, requestEntity, String.class);
            int statusCodeValue = exchange.getStatusCodeValue();
            if (statusCodeValue == 429) {
                HttpHeaders headers = exchange.getHeaders();
                String rate = headers.getFirst("X-RateLimit-Reset");
                log.info("429 Too Many Requests,X-RateLimit-Reset:{},{}秒后重新请求", rate, rate);
                return exchange;
            }
        } catch (Exception e) {
            log.error("[Request exception] err \n{} [Intf Name] \n{} \n [Intf Url] \n{} \n [Request Body]\n{}\n", e, api.getRemark(), address, json);
        } finally {
            long end = System.currentTimeMillis();
            body = null != exchange ? exchange.getBody() : "error";
            log.warn("[apiName]\n{}\n[IntfDesc]\n{}\n[IntfUrl]\n{}\n[RequestBody]\n{}\n[ResponseBody]\n{}\n[CostTime]\n{}ms\n", api.getApiName(), api.getRemark(), address, json, body, (end - beginTime));
        }
        return exchange;
    }

    public static RestBean post(SysApi api, String json, HttpHeaders httpHeaders) {

        return postJson(api, json, httpHeaders);
    }

    public static RestBean get(SysApi api, Map map, HttpHeaders headerInfo) {

        AtomicReference<String> stringAtomicReference = new AtomicReference<>("");
        map.forEach((k, v) -> stringAtomicReference.set(stringAtomicReference + "&" + k + "=" + v));
        String uri = stringAtomicReference.get();
        if (uri.startsWith("&")) {
            uri = uri.substring(1);
            uri = "?" + uri;
        }
        return get(api, uri, headerInfo);
    }


    public static ResponseEntity<String> exchange(RestTemplate restTemplate, HttpHeaders httpHeaders,
                                                  String address, HttpMethod method, String json) {
        ResponseEntity<String> exchange = null;
        String body;
        ThrowUtils.throwIf(StringUtils.isBlank(address),
                BusinessException.fail("address地址为空", SysSourceEnum.CSR_API.getSysName()));

        if (ObjectUtil.isEmpty(httpHeaders.getContentType())) {
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        }
        HttpEntity<String> requestEntity = new HttpEntity<>(json, httpHeaders);

        long beginTime = System.currentTimeMillis();
        try {
            if (httpHeaders.containsKey("POST")) {
                method = HttpMethod.POST;
            }
            exchange = restTemplate.exchange(address, method, requestEntity, String.class);
            int statusCodeValue = exchange.getStatusCodeValue();
            if (statusCodeValue == 429) {
                HttpHeaders headers = exchange.getHeaders();
                String rate = headers.getFirst("X-RateLimit-Reset");
                log.info("429 X-RateLimit-Reset:{},{}秒后重新请求", rate, rate);
                return exchange;
            }
        } catch (Exception e) {
            log.error("[Request exception] err \n" +
                    "{} \n [Intf Url] \n" +
                    "{} \n " +
                    "[Request Body]\n" +
                    "{}\n", e, address, json);
        } finally {
            long end = System.currentTimeMillis();
            body = null != exchange ? exchange.getBody() : "error";
            log.warn("[IntfUrl]\n{}\n[RequestBody]\n{}\n[ResponseBody]\n{}\n[CostTime]\n{}ms\n", address, json, body, (end - beginTime));
        }
        return exchange;
    }
}
